package com.svconnect.model.soap;

import java.io.StringReader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.http.HttpEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.protocol.HTTP;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.svconnect.model.config.BindingType;
import com.svconnect.serialization.XMLSerializer;
import com.svconnect.services.InvocationParam;
import com.svconnect.services.InvocationParamList;
import com.svconnect.util.Log;
import com.svconnect.util.StringUtil;

public class SoapRequestEnvelope {

	private static final String EMPTY_ENV = 
			"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
			"  <s:Header>" +
			"  </s:Header>" +
			"  <s:Body>" +
			"  </s:Body>" +
			"</s:Envelope>";
	
	private String action;
	private String methodName;
	private String serviceNamespace;
	private String serviceUrl;
	private BindingType binding;


	private InvocationParamList params;
	
	private Document envelope = null;
	
	public SoapRequestEnvelope(BindingType binding) {
		this.binding = binding;
	}
	
	
	private void init() {
		try {
			DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
			domFactory.setNamespaceAware(true);
			DocumentBuilder builder = domFactory.newDocumentBuilder();	
			InputSource is = new InputSource(new StringReader(EMPTY_ENV));
			envelope = builder.parse(is);
		}
		catch(Exception ex) {
			Log.e(ex);
		}
	}
	
	
	public void setParams(InvocationParamList params) {
		this.params = params;
	}
	
	public InvocationParamList getParams() {
		return this.params;
	}
	
	public String getAction() {
		return action;
	}
	public void setAction(String action) {
		this.action = action;
	}
	public String getMethodName() {
		return methodName;
	}
	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}
	public String getServiceNamespace() {
		return serviceNamespace;
	}
	public void setServiceNamespace(String serviceNamespace) {
		this.serviceNamespace = serviceNamespace;
	}
	public String getServiceUrl() {
		return serviceUrl;
	}

	public void setServiceUrl(String serviceUrl) {
		this.serviceUrl = serviceUrl;
	}	
	public BindingType getBinding() {
		return binding;
	}

	public void setBinding(BindingType binding) {
		this.binding = binding;
	}	
	
	@Override
	public String toString() {
		this.build();
		if (envelope!=null) {
			try {
				String xmlString = StringUtil.toXmlString(envelope);
				return xmlString.substring(xmlString.indexOf("\n")+1);
			}
			catch(Exception ex) {
				Log.e(ex);
			}
		}
		return null;
	}
	
	public HttpEntity getHttpEntity() {
		StringEntity entity = null;
		this.build();
		if (envelope!=null) {
			try {
				String xmlString = StringUtil.toXmlString(envelope);
				entity = new StringEntity(xmlString, HTTP.UTF_8);
				entity.setContentType("text/xml");
			}
			catch(Exception ex) {
				Log.e(ex);
			}
		}
		return entity;
	}
	
	private void build() {
		this.init();
		if (envelope!=null) {
			if (binding != BindingType.BasicHttpBinding) {
				appendAction();
				appendTo();
			}
			appendMethod();
			appendParams();
		}		
	}
	
	private void appendParams() {
		if (this.params!=null && this.methodName!=null) {
			
			XMLSerializer serializer = new XMLSerializer();
			serializer.setRootNamespace(serviceNamespace);
			serializer.setDocument(envelope);
			
			Element methodNode = (Element)envelope.getElementsByTagName(this.methodName).item(0);
			for(InvocationParam p : params) {
				Element elem = serializer.writeObject(p);
				methodNode.appendChild(elem);
			}
		}
	}
	
	private void appendTo() {
		if (this.serviceUrl!=null) {
			Element headerNode = (Element)envelope.getElementsByTagName("s:Header").item(0);
			Element toNode = envelope.createElement("To");
			toNode.setAttribute("s:mustUnderstand", "1");
			toNode.setAttribute("xmlns", "http://schemas.microsoft.com/ws/2005/05/addressing/none");
			toNode.setTextContent(this.serviceUrl);
			headerNode.appendChild(toNode);
		}
	}
	
	private void appendAction() {
		if (action!=null) {
			Element headerNode = (Element)envelope.getElementsByTagName("s:Header").item(0);
			Element actionNode = envelope.createElement("Action");
			actionNode.setAttribute("s:mustUnderstand", "1");
			actionNode.setAttribute("xmlns", "http://schemas.microsoft.com/ws/2005/05/addressing/none");
			actionNode.setTextContent(action);
			headerNode.appendChild(actionNode);
		}
	}
	
	private void appendMethod() {
		if (this.methodName!=null && this.serviceNamespace!=null) {
			Element bodyNode = (Element)envelope.getElementsByTagName("s:Body").item(0);
			NodeList bodyContentNodes = bodyNode.getChildNodes();
			for (int i=0; i<bodyContentNodes.getLength(); i++)
				bodyNode.removeChild(bodyContentNodes.item(i));
			
			Element methodNode = envelope.createElement(methodName);
			methodNode.setAttribute("xmlns", this.serviceNamespace);
			bodyNode.appendChild(methodNode);
		}
	}
	
}
